/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.thienloc.omap.service.broadcast;

import com.thienloc.omap.entity.BroadcastConfig;
import com.thienloc.omap.session.BroadcastFacade;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author GiangLT
 */
public class BroadcastManager {

    private static BroadcastManager instance;
    private BroadcastFacade broadcastFacade;
    public static Map<String, BroadcastUnit> broadcastUnitMap = new ConcurrentHashMap<String, BroadcastUnit>();
    private static final Logger logger = LoggerFactory.getLogger(BroadcastManager.class);

    public static BroadcastManager getInstance(BroadcastFacade broadcastFacade) {
        if (instance == null) {
            instance = new BroadcastManager(broadcastFacade);
        }
        return instance;
    }

    public BroadcastManager(BroadcastFacade broadcastFacade) {
        this.broadcastFacade = broadcastFacade;
    }

    public void addModule(BroadcastConfig config) throws Exception {
        String moduelName = config.getModuleName();
        if (broadcastUnitMap.get(moduelName) == null) {
            broadcastFacade.deleteByStatus("WAITING", config.getModuleName());
            logger.info("Add broadcast unit for {0}", moduelName);
            BroadcastUnit broadcastUnit = new BroadcastUnit(config);
            broadcastUnitMap.put(moduelName, broadcastUnit);
        }
    }

    public void startBroadcast(BroadcastConfig config) throws Exception {
        try {
            final String moduleName = config.getModuleName();
            addModule(config);

            int speed = config.getSpeed();
            int numOfWaiting = broadcastFacade.countByStatus("WAITING", config.getModuleName());
            final int totalTime = (numOfWaiting / speed) + 1;

            BroadcastProcessor processor = new BroadcastProcessor(broadcastFacade, config, broadcastUnitMap.get(moduleName).getSocket());
            final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
            broadcastUnitMap.get(moduleName).setExecutorService(executorService);

            logger.info("Start excuting");
            executorService.scheduleAtFixedRate(processor, 1, 1, TimeUnit.SECONDS);

            Runnable killerThread = new Runnable() {

                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                        if (!executorService.isShutdown() && !executorService.isTerminated()) {
                            executorService.shutdown();
                            logger.info("Shutdown Executor");
                        }
                    } catch (InterruptedException ex) {
                        logger.error("", ex);
                    }
                }
            };

            executorService.schedule(killerThread, totalTime, TimeUnit.SECONDS);
        } catch (Exception exception) {
            logger.error("", exception);
        }

    }

    public void stopBroadcast(String module) throws Exception {
        try {
            BroadcastUnit broadcastUnit = broadcastUnitMap.get(module);
            if (broadcastUnit != null) {
                ScheduledExecutorService executorService = broadcastUnit.getExecutorService();
                if (executorService != null && !executorService.isTerminated() && !executorService.isShutdown()) {
                    executorService.shutdown();
                    logger.info("Broadcast schedule stopped: {0}", module);
                } else {
                    logger.info("Broadcast schedule already stopped: {0}", module);
                }

                broadcastUnit.getSocket().close();
                logger.info("Closing socket: {0}", module);
            }
        } catch (Exception e) {
            throw e;
        } finally {
            broadcastUnitMap.remove(module);
            broadcastFacade.deleteByStatus("WAITING", module);
            logger.info("Delete all waiting messgae of: {0}", module);
        }
    }

    public void pauseBroadcast(String ipcModule) throws Exception {
        try {
            BroadcastUnit broadcastUnit = broadcastUnitMap.get(ipcModule);
            if (broadcastUnit != null) {
                broadcastUnit.getExecutorService().shutdown();
                logger.info("Broadcast paused: {0}", ipcModule);
            }
        } catch (Exception e) {
            throw e;
        }
    }

    public void setBroadcastFacade(BroadcastFacade broadcastFacade) {
        this.broadcastFacade = broadcastFacade;
    }

    public Map<String, BroadcastUnit> getBroadcastUnitMap() {
        return broadcastUnitMap;
    }
}
